package benchmarkapplication;

import database.Database;
import database.DatabasePool;
import entity.*;
import entitydao.TableDAO;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

class QuerySetPool {
    // values for each thread

    TableDAO[] mTableObjects;
    QuerySet[] mQuerySets;
    // values shared by all query records
    List<Method> mQueryMethods;
    List<Class[]> mMethodParameters;
    List<Integer> mQueryNumbers;
    List<Integer> mQueryMaximalNumbers;
    List<Integer> mDictId;
    List<String> mDictSurname;
    DatabasePool mDatabasePool;
    Database mInitDatabase;
    Random mRandom = new Random();
    int mFirstPosition = 0;
    //String mLine;
    public static int KNIHA_COUNT, AUTOR_COUNT, NAKLADATELSTVI_COUNT, NAVSTEVNIK_COUNT, PUJCENO_COUNT;
    public List<Kniha> knihaList;
    public List<Autor> autorList;
    public List<Nakladatelstvi> nakladatelstviList;
    public List<Navstevnik> navstevnikList;
    public List<Pujceno> pujcenoList;
    public Autor AUTOR;
    public Kniha KNIHA;
    private final Object lock = new Object();
    Random rng = new Random(Calendar.getInstance().getTime().getTime());

    public QuerySetPool(DatabasePool databasePool) {
        mDatabasePool = databasePool;
        mInitDatabase = new Database();
        mTableObjects = new TableDAO[databasePool.Count()];
        mQuerySets = new QuerySet[databasePool.Count()];
        for (int i = 0; i < databasePool.Count(); i++) {
            mQuerySets[i] = new QuerySet(databasePool.GetDatabase(i));
        }
        mQueryMethods = new ArrayList<Method>();
        mMethodParameters = new ArrayList<Class[]>();
        mQueryNumbers = new ArrayList<Integer>();
        mQueryMaximalNumbers = new ArrayList<Integer>();
    }

    /*
     * Read queries from the file.
     */
    public boolean ReadFromFile(String fileName) {
        boolean ret = true;
        File file = null;
        BufferedReader reader = null;
        String[] lineVal = null;
        String mLine;

        try {
            file = new File(fileName);
            reader = new BufferedReader(new FileReader(file));

            while ((mLine = reader.readLine()) != null) {
                System.out.println(mLine);

                if (mLine.startsWith("//")) {
                    continue;
                }

                lineVal = mLine.split("\t");

                String objectName = lineVal[0];
                SetAllTableObjects(objectName);

                String methodName = lineVal[1];

                int paramNumber = Integer.valueOf(lineVal[2]);
                Class[] types = new Class[paramNumber];
                for (int i = 0; i < paramNumber; i++) {
                    String dataType = lineVal[i + 3];
                    SetMethodDataType(i, types, dataType);
                }
                SetQueryMethod(objectName, methodName, types);
                mMethodParameters.add(types);

                // read the number of the query
                int maximalNumber = Integer.valueOf(lineVal[lineVal.length - 1]);
                mQueryNumbers.add(0);
                mQueryMaximalNumbers.add(maximalNumber);

                AddQueryRecords();
            }
        } catch (Exception e) {
            System.err.println(e.toString());
            ret = false;
        } finally {
            close(reader, file);
            mInitDatabase.Close();
        }
        return ret;
    }

    private void close(Closeable resource, File file) {
        if (resource != null) {
            try {
                resource.close();
            } catch (IOException e) {
                String message = "Closing file " + file.getPath() + " failed.";
                System.err.println(message);
            }
        }
    }

    private void SetAllTableObjects(String objectName) {
        for (int i = 0; i < mTableObjects.length; i++) {
            mTableObjects[i] = GetTableObject(i, objectName);
        }
    }

    private TableDAO GetTableObject(int index, String tableObject) {
        TableDAO table = null;

        if (tableObject.equals("Autor")) {
            table = mDatabasePool.GetDatabase(index).autorDAO;
        }
        if (tableObject.equals("Kniha")) {
            table = mDatabasePool.GetDatabase(index).knihaDAO;
        }
        if (tableObject.equals("Nakladatelstvi")) {
            table = mDatabasePool.GetDatabase(index).nakladatelstviDAO;
        }
        if (tableObject.equals("Navstevnik")) {
            table = mDatabasePool.GetDatabase(index).navstevnikDAO;
        }
         if (tableObject.equals("Pujceno")) {
            table = mDatabasePool.GetDatabase(index).pujcenoDAO;
        }
        return table;
    }

    public Autor getAutorAt(int index) {
        return autorList.get(index);
    }

    public Kniha getKnihaAt(int index) {
        return knihaList.get(index);
    }

    public Nakladatelstvi getNaklAt(int index) {
        return nakladatelstviList.get(index);
    }

    public Navstevnik getNavstevnikAt(int index){
        return navstevnikList.get(index);
    }

    public Pujceno getPujcenoAt(int index){
        return pujcenoList.get(index);
    }

    public Pujceno getPujceno(){
        Random rnd = new Random();
        return new Pujceno(rnd.nextInt(10000),rnd.nextInt(10000));
    }

    public Navstevnik getNavstevnik(){
        Random rnd = new Random();
        Navstevnik n = new Navstevnik();
        int randIndex = rnd.nextInt(QuerySetPool.NAVSTEVNIK_COUNT);
        n = this.getNavstevnikAt(randIndex);
        n.setJmeno("APPLICATION " + rnd.nextInt(QuerySetPool.NAVSTEVNIK_COUNT));
        n.setPrijmeni("APPLICATION " + rnd.nextInt(QuerySetPool.NAVSTEVNIK_COUNT));
        n.setLogin("APPLICATION " + rnd.nextInt(QuerySetPool.NAVSTEVNIK_COUNT));
        n.setPass("12345678");
        n.setMail(n.getPrijmeni()+"@neco.com");
        return n;
    }

    public Nakladatelstvi getNakladatelstvi() {
        Random rnd = new Random();
        Nakladatelstvi n = new Nakladatelstvi();
        int randIndex = rnd.nextInt(QuerySetPool.NAKLADATELSTVI_COUNT);
        n = this.getNaklAt(randIndex);
        n.setNazev("APPLICATION " + rnd.nextInt(QuerySetPool.NAKLADATELSTVI_COUNT));
        return n;
    }

    public Kniha getKniha() {
        Random rnd = new Random();
        Kniha k = new Kniha();
        int randIndex = rnd.nextInt(QuerySetPool.KNIHA_COUNT);
        k = this.getKnihaAt(randIndex);
        k.setNazev("APPLICATION " + rnd.nextInt(QuerySetPool.KNIHA_COUNT));
        return k;
    }

    private void SetQueryMethod(String tableObject, String methodName, Class[] types) {
        Method method = null;
        try {
            if (tableObject.equals("Autor")) {
                method = Class.forName("entitydao.AutorDAO").getMethod(methodName, types);
                if (autorList == null) {
                    autorList = mInitDatabase.autorDAO.findAll();
                }
                AUTOR_COUNT = autorList.size();
            }
            if (tableObject.equals("Kniha")) {
                method = Class.forName("entitydao.KnihaDAO").getMethod(methodName, types);
                if (knihaList == null) {
                    knihaList = mInitDatabase.knihaDAO.findAll();
                }
                KNIHA_COUNT = knihaList.size();
            }
            if (tableObject.equals("Nakladatelstvi")) {
                method = Class.forName("entitydao.NakladatelstviDAO").getMethod(methodName, types);
                if (nakladatelstviList == null) {
                    nakladatelstviList = mInitDatabase.nakladatelstviDAO.findAll();
                }
                NAKLADATELSTVI_COUNT = nakladatelstviList.size();
            }
            if (tableObject.equals("Navstevnik")) {
                method = Class.forName("entitydao.NavstevnikDAO").getMethod(methodName, types);
                if (navstevnikList == null) {
                    navstevnikList = mInitDatabase.navstevnikDAO.findAll();
                }
                NAVSTEVNIK_COUNT = navstevnikList.size();
            }
            if (tableObject.equals("Pujceno")) {
                method = Class.forName("entitydao.PujcenoDAO").getMethod(methodName, types);
                if (pujcenoList == null) {
                    pujcenoList = mInitDatabase.pujcenoDAO.findAll();
                }
                PUJCENO_COUNT = pujcenoList.size();
            }


            // Dodělat i ostatní tabulky
        } catch (NoSuchMethodException ex) {
            Logger.getLogger(QuerySetPool.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SecurityException ex) {
            Logger.getLogger(QuerySetPool.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(QuerySetPool.class.getName()).log(Level.SEVERE, null, ex);
        }

        mQueryMethods.add(method);
    }

    private void SetMethodDataType(int i, Class[] types, String dataType) {
        if (dataType.equals("int")) {
            types[i] = int.class;
        }
        if (dataType.equals("String")) {
            types[i] = String.class;
        }
        if (dataType.equals("Autor")) {
            types[i] = Autor.class;
        }
        if (dataType.equals("Kniha")) {
            types[i] = Kniha.class;
        }
        if (dataType.equals("Nakladatelstvi")) {
            types[i] = Nakladatelstvi.class;
        }
        if (dataType.equals("Navstevnik")) {
            types[i] = Navstevnik.class;
        }
        if (dataType.equals("Pujceno")) {
            types[i] = Pujceno.class;
        }
    }

    private void AddQueryRecords() {
        for (int i = 0; i < mTableObjects.length; i++) {
            mQuerySets[i].AddTableObject(mTableObjects[i]);
        }
    }

    public QuerySet GetQuerySet(int i) {
        return mQuerySets[i];
    }

    public Method GetQueryMethod(int index) {
        return mQueryMethods.get(index);
    }

    public Type[] GetMethodParameters(int index) {
        return mMethodParameters.get(index);
    }

    public int GetQueryNumber(int index) {
        return mQueryNumbers.get(index);
    }

    public int getDictId(int index) {
        return mDictId.get(index);
    }

    public String getDictSurname(int index) {
        return mDictSurname.get(index);
    }

    /// <summary>
    ///  Check if the queries were be extecuted.
    /// </summary>
    /// <returns></returns>
    public boolean CanIFinish() {
        boolean endf = true;
        synchronized (mQueryNumbers) {
            for (int i = 0; i < mQueryNumbers.size(); i++) {
                if (mQueryNumbers.get(i) < mQueryMaximalNumbers.get(i)) {
                    endf = false;
                    return false;
                }
            }
            return endf;
        }
    }

    /// <summary>
    ///  Check if it is possible to execute the current query.
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public boolean CanIExecuteQuery(int index, ThreadTest thread) {
        boolean flag = true;

        synchronized (mQueryNumbers) {
            if (mQueryNumbers.get(index) >= mQueryMaximalNumbers.get(index)) {
                flag = false;
            } else {
                int num = mQueryNumbers.get(index);
                num++;
                mQueryNumbers.set(index, num);
            }
            thread.queryNumber = mQueryNumbers.get(index);
        }
        return flag;
    }

    public String generateString(int length) {

        String characters = "qwertyuioplkjhgfdsazxcvbnm";
        char[] text = new char[length];
        for (int i = 0; i < length; i++) {
            text[i] = characters.charAt(rng.nextInt(characters.length()));
        }
        return new String(text);
    }

    public Autor getAutor() {
        AUTOR = new Autor();
        AUTOR.setJmeno(generateString(5));
        AUTOR.setPrijmeni(generateString(10));
        return AUTOR;
    }
}
